"use strict"

import * as A from '../actions/content-change'
import F, {set, spread, map, bind, reduce, eq, add, compose, useWith, identity, __, flatten, juxt, concat, slice, take, drop, split} from 'lodash/fp'
import {
    combineReducers
} from 'redux'

import record from '../operation-record'

const emptyTextElm = () => F.cloneDeep({
    content: '<p>这是一段新的文字</p>',
    css: {top: '100px', left: '100px', width: '150px', height: '26px', zIndex: '20'},
    classes: ['text', 'editable'],
    animations: []
}) 

const toPath = path => `pages.${path[0]}.elements.${path[1]}`

const toAbsPath = compose(toPath, split('.'))

const markEditable = (state, path) => {
    path = toAbsPath(path)+'.classes'
    let classes = F.get(path, state)
    return F.set(path, classes.includes('user-editable') ? classes : classes.concat('user-editable'), state)
}

const markC = (state, path) => {
    path = toAbsPath(path)+'.css'

    return F.set(path, Object.assign(F.get(path, state), {borderRadius: '50%'}), state)
}

const unMakr = (state, path) => {
    path = toAbsPath(path)+'.classes'
    let classes = F.get(path, state)
    , index = classes.indexOf('user-editable')
    return F.set(path, index === -1 ? classes : delIndex(index, classes), state)
}

const unMarkC = (state, path) => {
    path = toAbsPath(path)+'.css'

    return F.set(path, Object.assign(F.get(path, state), {borderRadius: '0'}), state)
}

const setContent = (content, path, state) => {
    let newElm = F.get(path, state)
    newElm.content = content
    newElm = F.assign(newElm, {})
    return F.set(path, newElm, state)
}

const either = (cond, t, f = F.identity) => F.cond([[cond, t],[F.T, f]])

const fjuxt = s => compose(flatten, juxt(s))

const reorderPages = (ordered, pages) => {
    // id -> page
    let find = (_, id) => F.find(['id', id], pages)

    //[id] -> id -> id
    let genId = F.flip(useWith(reduce(either(eq, add(F, '='))), [identity, map('id')]))

    let setP = compose(F.cloneDeep, spread(set('id')), juxt([genId, find]))
    
    return reduce(fjuxt([identity, setP]), [], ordered)
}

const delIndex = fjuxt([take, useWith(drop, [add(1)])])

const card = (state = {pages: [], activeIndex: 0, selectedPath: []}, action) => {
    let path, select, elms, index
    , setState = set(__, __, state)
    ,setPages = setState('pages')
    state.target = null

    let Record = record(action, state.pages)
    state.canUndo = Record.canUndo
    state.canRedo = Record.canRedo
    switch (action.type) {
        case A.CONTENT_CHANGE:
            return F.set('target', action.target, setContent(action.content, toPath(action.path), state))
        case A.FETCHED:
            return setPages(reorderPages(action.order, action.pages))
        case A.RE_ORDER:
            state.target = action.target
            state.focus = null
            state.selectedPath = []
            return set('pages', reorderPages(action.ordered, action.pages || state.pages), state)
        case A.SELELCT_ELM:
            path    = state.selectedPath
            select  = action.path.join('.')
            index   = path.indexOf(select)
            
            return setState( 'selectedPath'
                , action.ctrl
                    ? index === -1 ? concat(path, select) : delIndex(index, path)
                    : action.force || index === -1 || path.length > 1 ? [select] : []
            )
        case A.SWITCH_TO:
            return F.set('activeIndex', parseInt(action.index), state)
        case A.MARK_EDITABLE:
            return reduce(markEditable, state, action.path)
        case A.UNMARK_EDITABLE:
            return reduce(unMarkC, reduce(unMakr, state, action.path), action.path)
        case A.DELETE_ELM:
            //删除被选中的项
            state = F.omit(map(toAbsPath, action.path) //把elm的段路径map成绝对路径
                // 把已选中的路径清空
                , setState('selectedPath', []))

            state.focus = null
            
            return state
        case 'REST_TARGET':
            return F.set('target', null, state)
        case A.CSS_CHANGE:
            return reduce(markC, reduce(markEditable, state, action.path), action.path)
        case A.FOCUS_ELM:
            return state.focus === action.path ? state : F.set('isEditing', false, F.set('focus', action.path, state))
        case A.DRAGING:
            return F.set('dragOffset', {x: action.x, y: action.y}, F.set('isDraging', true, F.set('isEditing', false, state)))
        case A.DRAGED:
            return F.set('isDraging', false, state)
        case A.EDITING:
            return F.set('isEditing', true, state)
        case A.EDITED:
            return F.set('isEditing', false, state)
        case A.RESIZING:
            let focus = F.get(toAbsPath(state.focus), state)

            if (!focus || !focus.css) return state

            let {width: w, height: h, left: l, top: t} = focus.css

            let resize = {
                x: action.ev.screenX,
                y: action.ev.screenY,
                w: w ? parseInt(w) : 0,
                h: h ? parseInt(h) : 0,
                l: w ? parseInt(l) : 0,
                t: h ? parseInt(t) : 0,
                dot: action.dot,
            }
            return F.set('resize', resize, F.set('isResizing', true, state))
        case A.RESIZED:
            return F.set('isResizing', false, state)
        case A.STYLE_CHANGE:
                path = `${toAbsPath(action.path)}.css`
                return setState(path, Object.assign(F.get(path, state), action.style))
        case A.UNDO:
            state = setPages(Record.undo(state.pages))
            state.canUndo = Record.canUndo
            state.canRedo = Record.canRedo

            return state
        case A.REDO:
            state = setPages(Record.redo(state.pages))
            state.canUndo = Record.canUndo
            state.canRedo = Record.canRedo

            return state
        case A.ADD_TEXT_ELM:
            path = `pages.${state.activeIndex}.elements`
            elms = F.get(path, state)
            let len = elms.length
            state.focus = `${state.activeIndex}.${len}`

            let empty = emptyTextElm()

            for (let i = 0; i < len; i++) {
                if (elms[i] && elms[i].css && empty.css.top === elms[i].css.top) {
                    empty.css.top = `${Math.min(parseInt(empty.css.top) + 26, 460)}px`
                }
            }
            
            return setState(path, F.get(path, state).concat(empty))
        default:
            return state
    }
}

const gallery = (state = {photos: [],page: 0}, action) => {
    let {photos} = state
    switch (action.type) {
        case A.FILE_QUEUED:
            return F.assign(state, {photos: F.concat([action.file], photos), page: 0})
        case A.FILE_PROGRESS:
            return F.set(`photos.${F.findIndex(['id', action.id], photos)}.percentage`, action.percentage, state)
        case A.FILE_STATUS_CHANGE:
            if (action.status === 'success') {
                return F.set(`photos.${F.findIndex(['id', action.id], photos)}`, F.assign({status: 'uploaded', thumb: action.response.path + '/105'}, action.response), state)
            }
            return F.set(`photos.${F.findIndex(['id', action.id], photos)}.status`, action.status, state)
        case A.SELECT_PHOTO:
            return F.set('selectedId', action.id, state)
        case A.DELETED_PHOTO:
            return F.set('photos', state.photos.filter(t => t.id != action.id), state)
        case A.SWITCH_PAGE:
            return F.set('page', action.page, state)
        default:
            return state
    }
}

const music = (state = {search: {}}, action) => {
    let song
    , setList = F.set('userUpload.list', F.__, state)
    , list = F.get('userUpload.list', state, [])
    switch (action.type) {
        case 'KEYWORD_CHANGE':
            return F.set('search.keyword', action.key, state)
        case A.SEARCHING:
            return F.set('search.isSearching', true, state)
        case A.SEARCHED:
            return F.set('search.list', action.list, F.set('search.isSearching', false, state))
        case A.PLAY_MUSIC:
            return F.set('curSrc', action.src, state)
        case A.PUASE_MUSIC:
            return F.set('curSrc', '', state)
        case A.LOADED_MUSIC_SRC:
            return F.set(`search.list.${action.song.hash}`, action.song, state)
        case A.SWITCH_SEARCH_PAGE:
            return F.set(`search.curPage`, action.page, state)
        case A.UPLOADING_MUSIC:
            return setList(list.concat(action.file))
        case A.UPLOADED_MUSIC:
            let index = F.findIndex(['id', action.id.id], list)
            if (action.res.status === 'success') {
                return F.set('userUpload.list.'+index, action.res.song, state)
            } else {
                try {layer.alert(action.res.msg)} catch (e) {}
                return setList(delIndex(index, list))
            }
        case A.DELETEING_MUSIC:
            song = F.find(['id', action.id], list)
            song.status = 'uploading'
            return setList(list)
        case A.DELETED_MUSIC:
            return setList(list.filter(t => t.id != action.id))
        case A.SELECT_MUSIC:
            return action.song && action.song.status === 'uploading' ? state : F.set('selected', action.song, state)
        default:
            return state
    }
}

const global = (state = {}, action) => {
    switch (action.type) {
        case A.CROP_READY:
            return F.set('cropperReady', action.isReady, state)
        case A.MAP_CHANGE:
            return F.set('map', Object.assign(state.map || {}, action.map), state)
        case A.COVER_CHANG:
            return F.set('cover', action.cover, state)
        case A.$CROPPER_MODAL:
            return F.set('$cropper', action.$cropper, state)
        case A.SAVED_PAGES:
            return F.set('savedHash', window.hashCard(), state)
        default:
            return state
    }
}

export const reducers = combineReducers({
    card,
    gallery,
    music,
    mode: F.constant(window.MODE),
    global,
})